#pragma once
#include <common/cache_func_getter.h>
#include <common/common_utils.hpp>
#include <common/generic_model.hpp>
#include <common/uni_define.h>
#include <model/db/member/points_reward_item.h>
#include <nlohmann/json.hpp>
#include <string>

// DTO for points_reward_item table
struct PointsRewardItemDTO : public CacheFuncGetter {
  inline static const std::vector<std::string> required_fields = {
    "item_id",          //
    "item_name",        //
    "points_required",  //
    "item_quantity",    //
    "begin_time",       //
    "end_time"          //
  };

  in_id_type id = 0;
  std::string item_id = "";
  std::string item_name = "";
  double points_required = 0.0;
  double item_quantity = 0.0;
  datetime_type begin_time = {};
  datetime_type end_time = {};

  // JSON serialization/deserialization
  static PointsRewardItemDTO from_json(const nlohmann::json& j)
  {
    try {
      return PointsRewardItemDTO{
        .item_id = j.at("item_id").get<std::string>(),
        .item_name = j.at("item_name").get<std::string>(),
        .points_required = j.at("points_required").get<double>(),
        .item_quantity = j.at("item_quantity").get<double>(),
        .begin_time =
          utils::string_to_datetime(j.at("begin_time").get<std::string>()),
        .end_time =
          utils::string_to_datetime(j.at("end_time").get<std::string>())};
    }
    catch (const std::exception& e) {
      std::cerr << "[from_json error] " << e.what() << "\n"
                << "Input JSON: " << j.dump(2) << std::endl;
      throw;
    }
  }
};

inline void to_json(nlohmann::json& j,
                    const PointsRewardItemDTO& points_reward_item_dto)
{
  j = nlohmann::json{
    {"id", points_reward_item_dto.id},
    {"item_id", points_reward_item_dto.item_id},
    {"item_name", points_reward_item_dto.item_name},
    {"points_required", points_reward_item_dto.points_required},
    {"item_quantity", points_reward_item_dto.item_quantity},
    {"begin_time",
     utils::datetime_to_string(points_reward_item_dto.begin_time)},
    {"end_time", utils::datetime_to_string(points_reward_item_dto.end_time)}};
}

// ORM mapping
namespace model {
template <> struct ReflectTable<PointsRewardItemDTO, db::points_reward_item> {
  static constexpr auto map_members = std::make_tuple(
    std::make_pair(&PointsRewardItemDTO::id, &db::points_reward_item::id),
    std::make_pair(&PointsRewardItemDTO::item_id,
                   &db::points_reward_item::item_id),
    std::make_pair(&PointsRewardItemDTO::item_name,
                   &db::points_reward_item::item_name),
    std::make_pair(&PointsRewardItemDTO::points_required,
                   &db::points_reward_item::points_required),
    std::make_pair(&PointsRewardItemDTO::item_quantity,
                   &db::points_reward_item::item_quantity),
    std::make_pair(&PointsRewardItemDTO::begin_time,
                   &db::points_reward_item::begin_time),
    std::make_pair(&PointsRewardItemDTO::end_time,
                   &db::points_reward_item::end_time));
};

// mapping
template <typename Points_reward_itemRow>
struct ReflectTableRow<PointsRewardItemDTO, Points_reward_itemRow> {
  static PointsRewardItemDTO assign_model(Points_reward_itemRow&& row)
  {
    return PointsRewardItemDTO{.id = row.id,
                               .item_id = row.item_id,
                               .item_name = row.item_name,
                               .points_required = row.points_required,
                               .item_quantity = row.item_quantity,
                               .begin_time = row.begin_time,
                               .end_time = row.end_time};
  }
};
}  // namespace model
